﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Windows;

using ABCat.Core;
using ABCat.Core.Plugins.Catalog.GrouppingLogics;
using ABCat.Shared.Properties;

namespace ABCat.UI.WPF.Models
{
	public sealed class GrouppingLogicViewModel : INotifyPropertyChanged
	{
		#region + Events +

		public event PropertyChangedEventHandler PropertyChanged;

		#endregion
		#region + Fields +

		private readonly Action<Group> _selectedGroupChanged;

		private CancellationTokenSource _cancellationTokenSource;
		private IEnumerable<IGrouppingLogicPlugin> _grouppingPlugins;
		private bool _isOnUpdate;
		private IEnumerable<Group> _root;
		private Group _selectedGroup;
		private IGrouppingLogicPlugin _selectedGrouppingLogicPlugin;

		#endregion
		#region + Properties +

		public IEnumerable<IGrouppingLogicPlugin> GrouppingPlugins
		{
			get { return _grouppingPlugins; }
			set
			{
				if (Equals(value, _grouppingPlugins)) return;
				_grouppingPlugins = value;
				OnPropertyChanged();
			}
		}

		public bool IsOnUpdate
		{
			get { return _isOnUpdate; }
			set
			{
				if (value.Equals(_isOnUpdate)) return;
				_isOnUpdate = value;
				OnPropertyChanged();
			}
		}

		public IEnumerable<Group> Root
		{
			get { return _root; }
			set
			{
				if (Equals(value, _root)) return;
				_root = value;
				OnPropertyChanged();
			}
		}

		public Group SelectedGroup
		{
			get { return _selectedGroup; }
			set
			{
				if (Equals(value, _selectedGroup)) return;
				_selectedGroup = value;
				if (_selectedGroupChanged != null) _selectedGroupChanged(value);
				OnPropertyChanged();
			}
		}

		public IGrouppingLogicPlugin SelectedGrouppingLogicPlugin
		{
			get { return _selectedGrouppingLogicPlugin; }
			set
			{
				if (Equals(value, _selectedGrouppingLogicPlugin)) return;
				_selectedGrouppingLogicPlugin = value;
				OnPropertyChanged();
				BeginGenerateGroupsAsync(value);
			}
		}

		#endregion
		#region + Ctor +

		public GrouppingLogicViewModel(IEnumerable<IGrouppingLogicPlugin> grouppingPlugins, Action<Group> selectedGroupChanged)
		{
			_selectedGroupChanged = selectedGroupChanged;
			GrouppingPlugins = grouppingPlugins;
			SelectedGrouppingLogicPlugin = GrouppingPlugins.FirstOrDefault();
		}

		#endregion
		#region + Logic +

		public void Refresh()
		{
			BeginGenerateGroupsAsync(SelectedGrouppingLogicPlugin);
		}

		private void BeginGenerateGroupsAsync(IGrouppingLogicPlugin grouppingLogic)
		{
			var cancellationTokenSource = _cancellationTokenSource;
			if (cancellationTokenSource != null) cancellationTokenSource.Cancel();
			cancellationTokenSource = new CancellationTokenSource();
			_cancellationTokenSource = cancellationTokenSource;
			grouppingLogic.BeginGenerateGroupsAsync((group, ex) =>
				{
					try
					{
						IsOnUpdate = true;
						if (ex != null) throw new Exception("Ошибка генерации групп", ex);

						Root = new[] { group };
						if (group != null)
						{
							group.IsSelected = true;
							group.IsExpanded = true;
						}

						SelectedGroup = group;
					}
					finally
					{
						IsOnUpdate = false;
					}
				}, cancellationTokenSource.Token);
		}

		[NotifyPropertyChangedInvocator]
		private void OnPropertyChanged([CallerMemberName] string propertyName = null)
		{
			PropertyChangedEventHandler handler = PropertyChanged;
			if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
		}

		#endregion
	}
}